Skip to content

Feat/state diff panel#1091

Open
ezedike-evan wants to merge 4 commits intodotandev:mainfrom
ezedike-evan:feat/state-diff-panel
Open

Feat/state diff panel#1091
ezedike-evan wants to merge 4 commits intodotandev:mainfrom
ezedike-evan:feat/state-diff-panel

Conversation

@ezedike-evan
Copy link
Copy Markdown
Contributor

================================================================================
TITLE:

feat(ui): Implement State Diff Panel in Interactive Mode - Issue #1006

DESCRIPTION:

Overview

Implements a resizable, scrollable three-column State Diff panel for the
interactive split-screen trace viewer. The panel shows exactly what changed in
ExecutionState.HostState between consecutive execution steps, with colour-coded
additions, removals, and modifications.

Changes

Core Implementation

  • StatePanel (internal/ui/widgets/state_panel.go): New diff widget

    • ComputeDiff(prev, curr *ExecutionState) diffs HostState map[string]interface{}
      between two consecutive steps — the native state change structure in the codebase
    • DiffKind enum: DiffAdded (green +), DiffRemoved (red -),
      DiffChanged (yellow ~), DiffSame (dim) — with stable sort priority:
      changed → added → removed → same, alphabetical within groups
    • StatePanel.Lines(width, maxRows int) renders the 3-column table
      (indicator │ Key │ Old Value │ New Value) into a []string slice the
      layout consumes directly
    • Proportional column widths: 30% key, 35% old value, 35% new value
    • SelectUp/Down() with scrollTop tracking for full keyboard scrollability
    • Summary() returns +N -N ~N count string for status bars
    • SetNoColor(bool) for piped/non-TTY output
  • Styles (internal/ui/styles.go): New centralised styling module

    • Single source of truth for all ANSI SGR codes used across the ui package
    • Colorize(text, style string) with NoColor global for piped output
    • DefaultBorder struct for consistent border character sets
    • widgets package exports its own Colorize copy to keep the dependency
      graph acyclic (widgets → trace only, not widgets → ui)
  • Layout Extension (internal/ui/layout.go): Three-pane support

    • PaneDiff constant added to the Pane iota alongside existing
      PaneTrace and PaneState
    • ShowDiff bool field and ToggleDiff() method — hides diff panel and
      moves focus to PaneState if diff panel had focus when closed
    • MiddleWidth() / RightWidth() — two-pane: middle fills all remaining
      space, right returns 0; three-pane: remaining split 50/50
    • Render() signature extended to Render(leftLines, middleLines, rightLines []string);
      top/bottom borders extend automatically when ShowDiff is true
    • SplitRatio adjusted from 0.5 → 0.4 to give equal breathing room across
      three panes at typical terminal widths
  • TraceView Wiring (internal/ui/trace_view.go): Diff panel integration

    • diffPanel *widgets.StatePanel field added to TraceView
    • NewTraceView now accepts etrace *trace.ExecutionTrace (nil-safe) —
      pass nil to disable the diff panel without breaking existing callers
    • refreshDiff() looks up etrace.States[currentStep] and
      States[currentStep-1], passes both to diffPanel.SetStates(prev, curr);
      step 0 passes prev=nil so all keys appear as DiffAdded
    • refreshDiff() called alongside refreshState() on every tree navigation
      event so the diff panel always reflects the selected step
    • HandleKey routes KeyDiff to ToggleDiff() and dispatches PaneDiff
      Up/Down events to diffPanel.SelectUp/Down()
  • Key Binding (internal/ui/keys.go): Diff toggle key

    • KeyDiff constant added to the Key iota
    • 'd' / 'D' mapped to KeyDiff in KeyReader.Read()

Integration

Update NewTraceView call site to pass the ExecutionTrace:

// Before:
tv := ui.NewTraceView(root)

// After (pass nil if ExecutionTrace is unavailable):
tv := ui.NewTraceView(root, v.trace)

Toggle the diff panel at runtime with d. Navigate it with / after
switching focus with Tab or .

Testing

  • Diff panel renders correctly at 80, 120, and 220 column widths
  • ComputeDiff correctly classifies added, removed, changed, and unchanged keys
  • Step 0 (no previous state) shows all keys as DiffAdded
  • ToggleDiff hides panel and reassigns focus when PaneDiff was active
  • Scrolling and row selection work independently from the trace and state panes
  • Summary() returns correct +N -N ~N counts across all diff kinds

Related Issues

Closes #1006

Type of Change

  • New feature

Checklist

  • Code follows project style guidelines
  • Self-review completed
  • No new linting issues
  • Changes verified locally
    ================================================================================

- Add internal/ui/widgets/state_panel.go — DiffKind (added/removed/changed/same),
  ComputeDiff() diffs ExecutionState.HostState maps; StatePanel renders a
  3-column table (indicator│Key│Old Value│New Value) with colour coding,
  proportional column widths, scrolling, and a Summary() status line
- Add internal/ui/styles.go — centralised ANSI colour constants, Colorize(),
  NoColor global, DefaultBorder; widgets package exports its own copy to
  keep the dependency graph acyclic
- Update internal/ui/layout.go — add PaneDiff pane constant, ShowDiff field,
  ToggleDiff(), MiddleWidth()/RightWidth() for three-pane column arithmetic,
  extend Render() signature to accept leftLines/middleLines/rightLines
- Update internal/ui/trace_view.go — add diffPanel field and etrace param to
  NewTraceView; refreshDiff() wires ExecutionTrace steps to StatePanel.SetStates;
  HandleKey routes KeyDiff to ToggleDiff and PaneDiff nav to panel scroll
- Update internal/ui/keys.go — add KeyDiff constant, map 'd'/'D' to it
@drips-wave
Copy link
Copy Markdown

drips-wave bot commented Mar 30, 2026

@ezedike-evan Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@dotandev
Copy link
Copy Markdown
Owner

fix ci.

Copy link
Copy Markdown
Owner

@dotandev dotandev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm.

@dotandev
Copy link
Copy Markdown
Owner

fix ci, tho.

@ezedike-evan
Copy link
Copy Markdown
Contributor Author

fix ci, tho.

the CI has been fixed

@ezedike-evan
Copy link
Copy Markdown
Contributor Author

@dotandev please your workflow are flagging files i didn't work on

please review what is wrong

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[TIME-TRAVEL] [UI] Implement 'State Diff' panel in interactive mode

2 participants